
#include "base/operate_define.h"
#include "base_util.h"
#include "codec_config.h"
#include "dump.h"
#include "input_output_context.h"
#include "message_queue.h"
#include "packet_frame_operate.h"
#include "test_code.h"
#include "format_translate.h"
#include <cstdio>
#include <cstdlib>
#include <memory>
#include <stdexcept>
#include <thread>
#include <unistd.h>

struct MessageQueue
{
	PacketMessageQueue decode_packet_message_queue;
	FrameMessageQueue  frame_message_queue;
	PacketMessageQueue encode_video_packet_message_queue;
	PacketMessageQueue encode_audio_packet_message_queue;
};

void ReadFilePacketData(InputFormatContext & input_format_ctx, MessageQueue & message_queue)
{
	AVPacket * packet = av_packet_alloc();
	ReadPacketDataToMessageQueue(&input_format_ctx, packet, message_queue.decode_packet_message_queue);
}

void ProcessEncodePacketData(OutputFormatContext & output_ctx, PacketMessageQueue & message_queue, bool is_video, double & audio_time, double & video_time)
{
	for (;true;)
	{
		AVPacket * packet = nullptr;
		int result = message_queue.PopPacketNonBlocking(&packet);
		if (result < 0)
		{
			break;
		}
		if (is_video)
		{
			video_time = packet->dts * av_q2d(output_ctx.VideoEncoder()->AvCodecCtx()->time_base);
			packet->stream_index = 0;
		}
		else
		{
			audio_time = packet->dts * av_q2d(output_ctx.AudioEncoder()->AvCodecCtx()->time_base);
			packet->stream_index = 1;
		}
		printf("is video : %d pts %8ld and dts : %8ld.\n", is_video, packet->pts, packet->dts);
		
		av_interleaved_write_frame(output_ctx.AvFormatContext(), packet);

		av_packet_free(&packet);
		if (audio_time > video_time)
		{
			break;
		}
	}
}

void ProcessData(InputFormatContext & input_format_ctx, OutputFormatContext & output_format_ctx)
{
	MessageQueue message_queue;
	message_queue.decode_packet_message_queue.InitMessageQueue(500);
	message_queue.frame_message_queue.InitMessageQueue(1000);
	message_queue.encode_video_packet_message_queue.InitMessageQueue(500);
	message_queue.encode_audio_packet_message_queue.InitMessageQueue(500);

	std::thread thd = std::thread([&message_queue, &input_format_ctx](){

		ReadFilePacketData(input_format_ctx, message_queue);
	});

	double video_time = 0.0;
	double audio_time = 0.0;

	output_format_ctx.WriterHeaderTailer();
	for (;true;)
	{
		AVPacket * pkt = nullptr;
		int ret = message_queue.decode_packet_message_queue.PopPacketNonBlocking(&pkt);
		if (ret < 0)
		{
			if (ret == AVERROR_EOF)
			{
				break;
			}
			usleep(1000);
			continue;
		}
		bool is_video = false;
		if (pkt->stream_index == 0)
		{
			DecodePacketDataToMessageQueue(input_format_ctx.VideoDecoder(), pkt, message_queue.frame_message_queue);
			is_video = true;
		}
		else
		{
			DecodePacketDataToMessageQueue(input_format_ctx.AudioDecoder(), pkt, message_queue.frame_message_queue);
		}
		for (;true;)
		{
			AVFrame * frame = nullptr;
			ret = message_queue.frame_message_queue.PopFrameNonBlocking(&frame);
			if (ret < 0)
			{
				break;
			}
			if (is_video)
			{
				EncodeFrameDataToMessageQueue(output_format_ctx.VideoEncoder(), frame, message_queue.encode_video_packet_message_queue);
			}
			else
			{
				EncodeFrameDataToMessageQueue(output_format_ctx.AudioEncoder(), frame, message_queue.encode_audio_packet_message_queue);
			}
			if (audio_time > video_time)
			{
				// process video packet data
				ProcessEncodePacketData(output_format_ctx, message_queue.encode_video_packet_message_queue, true, audio_time, video_time);
			}
			else
			{
				// process audio packet data
				ProcessEncodePacketData(output_format_ctx, message_queue.encode_audio_packet_message_queue, false, audio_time, video_time);
			}
			av_frame_free(&frame);
		}
		av_packet_free(&pkt);
	}

	int size = message_queue.frame_message_queue.GetSize();
	if (size > 0)
	{
		PrintLog("frame message queue is not empty");
	}
	else
	{
		DecodePacketDataToMessageQueue(input_format_ctx.VideoDecoder(), nullptr, message_queue.frame_message_queue);

		ProcessFrameDataFromMessageQueue(message_queue.frame_message_queue,[&output_format_ctx, &message_queue](AVFrame * frame){

			EncodeFrameDataToMessageQueue(output_format_ctx.VideoEncoder(), frame, message_queue.encode_video_packet_message_queue);
			while (true)
			{
				AVPacket * pkt = nullptr;
				int ret = message_queue.encode_video_packet_message_queue.PopPacketNonBlocking(&pkt);
				if (ret < 0)
				{
					break;
				}
				pkt->stream_index = output_format_ctx.VideoMediaStream()->Index();
				av_interleaved_write_frame(output_format_ctx.AvFormatContext(), pkt);
			}
		}, false);
		EncodeFrameDataToMessageQueue(output_format_ctx.VideoEncoder(), nullptr, message_queue.encode_video_packet_message_queue);
		while (true)
		{
			AVPacket * pkt = nullptr;
			int ret = message_queue.encode_video_packet_message_queue.PopPacketNonBlocking(&pkt);
			if (ret < 0)
			{
				break;
			}
			pkt->stream_index = output_format_ctx.VideoMediaStream()->Index();
			av_interleaved_write_frame(output_format_ctx.AvFormatContext(), pkt);
		}

		DecodePacketDataToMessageQueue(input_format_ctx.AudioDecoder(), nullptr, message_queue.frame_message_queue);
		ProcessFrameDataFromMessageQueue(message_queue.frame_message_queue, [&output_format_ctx, &message_queue](AVFrame * frame){

			EncodeFrameDataToMessageQueue(output_format_ctx.AudioEncoder(), frame, message_queue.encode_audio_packet_message_queue);
			while (true)
			{
				AVPacket * pkt = nullptr;
				int ret = message_queue.encode_audio_packet_message_queue.PopPacketNonBlocking(&pkt);
				if (ret < 0)
				{
					break;
				}
				pkt->stream_index = output_format_ctx.AudioMediaStream()->Index();
				av_interleaved_write_frame(output_format_ctx.AvFormatContext(), pkt);
			}
		}, false);
		EncodeFrameDataToMessageQueue(output_format_ctx.AudioEncoder(), nullptr, message_queue.encode_audio_packet_message_queue);
		while (true)
		{
			AVPacket * pkt = nullptr;
			int ret = message_queue.encode_audio_packet_message_queue.PopPacketNonBlocking(&pkt);
			if (ret < 0)
			{
				break;
			}
			pkt->stream_index = output_format_ctx.AudioMediaStream()->Index();
			av_interleaved_write_frame(output_format_ctx.AvFormatContext(), pkt);
		}
	}
	av_write_trailer(output_format_ctx.AvFormatContext());
	if (thd.joinable())
	{
		thd.join();
	}
}

int main(int argc, const char * argv[])
{
	if (argc < 2)
	{
		PrintLog("need input filename param");
		return EXIT_FAILURE;
	}
	std::string input_filename = argv[1];
	std::string output_filename = "./test.mp4";

    QsvDecodeAndUseFilterTranslateFormat(input_filename);
	// TestHwDecodeVideoData(input_filename);

	// InputFormatContext input_ctx;

	// input_ctx.OpenUrlWithFindStreamInfo(input_filename.c_str());
	// input_ctx.CreateAudioDecoderCtx("", CodecConfig{});
	// input_ctx.CreateVideoDecoderCtx("", CodecConfig{});

	// OutputFormatContext output_ctx;

	// output_ctx.CreateUrl(output_filename.c_str());

	// output_ctx.CreateVideoStream(input_ctx.VideoMediaStream(), CodecConfig{});
	// output_ctx.CreateAudioStream(input_ctx.AudioMediaStream(), CodecConfig{});

	// ProcessData(input_ctx, output_ctx);

	return EXIT_SUCCESS;
}